using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Threading;
using Ionic.Zip;
using Sedge.Core.App;
using Sedge.Core.Configuration;
using Sedge.Core.Configuration.Report;
using Sedge.Core.Execution.ReportElements;
using Sedge.Core.FileSystemWrap;
using Sedge.Core.Globalization;

namespace Sedge.Core.Execution
{
	public class ReportGenerator
	{
		private readonly Suite _suite;
		private int _stepsCount;
		private int _currentStep;
		private BackgroundWorker _worker;
		private readonly List<Page> _pages = new List<Page>();

		public ReportGenerator(Suite suite)
		{
			_suite = suite;
			_suite.MergeSuiteWithSelectedApp();
		}

		public int GetStepsCount()
		{
			_stepsCount = CalculateSources() + _suite.FilesRegistry.Count + 6;
			return _stepsCount;
		}

		public void Run(BackgroundWorker worker)
		{
			_worker = worker;
			CopyTemplateFiles();
			CreateIndexFile();
			MoveRegisteredFiles();
			CollectData();
			ZipData();

			if (_currentStep < _stepsCount)
				_currentStep = _stepsCount;
			ReportProgress(Locale.Strings.CollectStepReady);
		}

		private void ZipData()
		{
			ReportProgress(Locale.Strings.CollectZipData);
			IDirectoryInfoWrap directoryInfo = Services.FileSystem.Directory.GetDirectoryInfo(_suite.WorkingFolder);

			using (ZipFile zipFile = new ZipFile())
			{
				zipFile.AddDirectory(_suite.WorkingFolder, directoryInfo.Name);
				_suite.ZipFile = Services.FileSystem.Path.Combine(_suite.WorkingFolder,
																				  String.Concat(directoryInfo.Name, ".zip"));
				zipFile.Save(_suite.ZipFile);
			}
		}

		private void MoveRegisteredFiles()
		{
			_suite.FilesRegistry.CopyFiles(OneFileCopyReport);
		}

		private bool OneFileCopyReport(string file)
		{
			ReportProgress(file);
			return !_worker.CancellationPending;
		}

		private void CollectData()
		{
			ReportProgress(Locale.Strings.CollectStepData);

			string templatePath = Services.FileSystem.Path.Combine(Locale.TemplateFolder, Branding.ReportHtmlFiles);
			string templateFile = Services.FileSystem.Path.Combine(templatePath, "page.template");
			string templateStr = Services.FileSystem.File.ReadAllText(templateFile);

			foreach (Page page in _pages)
			{
				List<IReportElement> pageElements = new List<IReportElement>();
				foreach (ISource source in page.Sources)
				{
					if (_worker.CancellationPending)
						return;

					ReportProgress(String.Format("{0}: {1}", Locale.Strings.CollectStepData, page.Caption));
					pageElements.AddRange(source.GetReportElements());
				}

				StringBuilder pageBuilder = new StringBuilder();
				IReportElement mergedElement = null;
				foreach (IReportElement element in pageElements)
				{
					if (mergedElement == null)
					{
						mergedElement = element;
						continue;
					}

					if (mergedElement.CanMergeWith(element))
					{
						mergedElement.MergeWith(element);
					}
					else
					{
						pageBuilder.Append(mergedElement.GetHtml());
						mergedElement = element;
					}
				}

				if (mergedElement != null)
				{
					pageBuilder.Append(mergedElement.GetHtml());
				}

				StringBuilder sb = new StringBuilder(templateStr);
				sb.Replace("${header}", page.Caption);
				sb.Replace("${content}", pageBuilder.ToString());
				Services.FileSystem.File.WriteAllText(
					 Services.FileSystem.Path.Combine(_suite.WorkingFolder, page.FileName), sb.ToString());
			}
		}

		private void ReportProgress(string status)
		{
			_currentStep++;
			_worker.ReportProgress(_currentStep, status);
			Thread.Sleep(100);
		}

		private void CopyTemplateFiles()
		{
			ReportProgress(Locale.Strings.CollectStepCopyTemplate);

			string path = Services.FileSystem.Path.Combine(Locale.TemplateFolder, Branding.ReportHtmlFiles);
			string[] dirs = Services.FileSystem.Directory.GetDirectories(path);
			foreach (string dir in dirs)
			{
				if (_worker.CancellationPending)
					return;
				if (dir.Contains(".svn"))
					continue;

				IDirectoryInfoWrap directoryInfo = Services.FileSystem.Directory.GetDirectoryInfo(dir);
				string destPath = Services.FileSystem.Path.Combine(_suite.HtmlFilesFolder, directoryInfo.Name);
				if (!Services.FileSystem.Directory.Exists(destPath))
				{
					Services.FileSystem.Directory.CreateDirectory(destPath);
					string[] files = Services.FileSystem.Directory.GetFiles(dir);
					foreach (string file in files)
					{
						string destFilePath = Services.FileSystem.Path.Combine(destPath, Services.FileSystem.Path.GetFileName(file));
						Services.FileSystem.File.Copy(file, destFilePath);
					}
				}
			}
		}

		private void CreateIndexFile()
		{
			if (_worker.CancellationPending)
				return;

			ReportProgress(Locale.Strings.CollectStepCreateIndexTemplate);

			int groupNum = 1;
			int pageNum = 1;
			StringBuilder menuBuilder = new StringBuilder();

			foreach (Group group in _suite.Groups)
			{
				menuBuilder.Append("<div class=\"menu-group\">");
				menuBuilder.AppendFormat("<div class=\"menu-group-header\">{0}</div>", group.Caption);
				menuBuilder.Append("<ul class=\"menu-list\">");

				foreach (Page page in group.Pages)
				{
					string pageName = String.Format("page_{0:D2}_{1:D2}.html", groupNum, pageNum);
					string pagePath = Branding.ReportHtmlFiles + "/" + pageName;
					menuBuilder.AppendFormat(
						"<li class=\"menu-item\"><a class=\"menu-link\" href=\"{0}\" target=\"contentFrame\">{1}</a></li>",
						pagePath, page.Caption);

					foreach (ISource source in page.Sources)
					{
						source.PrepareSource(_suite);
					}

					page.FileName = pagePath;
					_pages.Add(page);
					pageNum++;
				}
				menuBuilder.Append("</ul></div>");
				groupNum++;
			}

			string template = Services.FileSystem.Path.Combine(Locale.TemplateFolder, "index.template");
			StringBuilder sb = new StringBuilder(Services.FileSystem.File.ReadAllText(template));
			sb.Replace("${menu}", menuBuilder.ToString());
			sb.Replace("${title}", _suite.Caption);
			Services.FileSystem.File.WriteAllText(
							Services.FileSystem.Path.Combine(_suite.WorkingFolder, "index.html"), sb.ToString());
		}

		private int CalculateSources()
		{
			int sourcesCount = 0;
			foreach (Group group in _suite.Groups)
			{
				foreach (Page page in group.Pages)
				{
					sourcesCount += page.Sources.Count;
				}
			}
			return sourcesCount;
		}
	}
}